package com.im.service.message.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.im.service.config.AppConfig;
import com.im.common.constant.Constants;
import com.im.common.enums.ConversationTypeEnum;
import com.im.common.enums.DelFlagEnum;
import com.im.common.model.message.*;
import com.im.service.conversation.service.ConversationService;
import com.im.service.group.entity.ImGroupMessageHistoryEntity;
import com.im.service.group.mapper.ImGroupMessageHistoryMapper;
import com.im.service.message.entity.ImMessageBodyEntity;
import com.im.service.message.entity.ImMessageHistoryEntity;
import com.im.service.message.mapper.ImMessageBodyMapper;
import com.im.service.message.mapper.ImMessageHistoryMapper;
import com.im.service.message.service.MessageStoreService;
import com.im.service.utils.SnowflakeIdWorker;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 消息存储服务类，用于将消息存入 Redis 中
 */
@Service
public class MessageStoreServiceImpl implements MessageStoreService {

    @Autowired
    private ImMessageHistoryMapper imMessageHistoryMapper;
    @Autowired
    private ImMessageBodyMapper imMessageBodyMapper;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private ImGroupMessageHistoryMapper imGroupMessageHistoryMapper;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private ConversationService conversationService;
    @Autowired
    private AppConfig appConfig;

    /**
     * 存储单人聊天消息
     *
     * @param messageContent 聊天消息内容
     * @return void
     */
    @Transactional
    public void storeP2PMessage(MessageContent messageContent) {
        // 提取消息体
        ImMessageBody imMessageBodyEntity = extractMessageBody(messageContent);
        // 封装存储点对点消息的数据传输对象
        DoStoreP2PMessageDto dto = new DoStoreP2PMessageDto();
        dto.setMessageContent(messageContent);
        dto.setMessageBody(imMessageBodyEntity);
        // 设置消息的唯一标识
        messageContent.setMessageKey(imMessageBodyEntity.getMessageKey());
        // 发送消息到 RabbitMQ 中，等待消费
        rabbitTemplate.convertAndSend(Constants.RabbitConstants.StoreP2PMessage, "", JSONObject.toJSONString(dto));
    }

    /**
     * 将消息内容转化为消息体
     *
     * @param messageContent 聊天消息内容
     * @return ImMessageBody 消息体
     */
    public ImMessageBody extractMessageBody(MessageContent messageContent) {
        ImMessageBody messageBody = new ImMessageBody();
        messageBody.setAppId(messageContent.getAppId()); // 设置应用 ID
        messageBody.setMessageKey(snowflakeIdWorker.nextId()); // 生成消息 ID
        messageBody.setCreateTime(System.currentTimeMillis()); // 设置消息创建时间
        messageBody.setSecurityKey(""); // 设置安全密钥
        messageBody.setExtra(messageContent.getExtra()); // 设置额外数据
        messageBody.setDelFlag(DelFlagEnum.NORMAL.getCode()); // 设置删除标志
        messageBody.setMessageTime(messageContent.getMessageTime()); // 设置消息发送时间
        messageBody.setMessageBody(messageContent.getMessageBody()); // 设置消息内容
        return messageBody; // 返回 ImMessageBody 对象
    }

    /**
     * 将消息内容转化为单人聊天消息历史记录
     *
     * @param messageContent      聊天消息内容
     * @param imMessageBodyEntity 消息体
     * @return List<ImMessageHistoryEntity> 消息历史记录列表
     */
    public List<ImMessageHistoryEntity> extractToP2PMessageHistory(MessageContent messageContent, ImMessageBodyEntity imMessageBodyEntity) {
        List<ImMessageHistoryEntity> list = new ArrayList<>();
        // 构建发送方的消息历史记录实体
        ImMessageHistoryEntity fromHistory = new ImMessageHistoryEntity();
        BeanUtils.copyProperties(messageContent, fromHistory);
        fromHistory.setOwnerId(messageContent.getFromId());
        fromHistory.setMessageKey(imMessageBodyEntity.getMessageKey());
        fromHistory.setCreateTime(System.currentTimeMillis());
        // 构建接收方的消息历史记录实体
        ImMessageHistoryEntity toHistory = new ImMessageHistoryEntity();
        BeanUtils.copyProperties(messageContent, toHistory);
        toHistory.setOwnerId(messageContent.getToId());
        toHistory.setMessageKey(imMessageBodyEntity.getMessageKey());
        toHistory.setCreateTime(System.currentTimeMillis());
        // 将发送方和接收方的消息历史记录实体添加到列表中
        list.add(fromHistory);
        list.add(toHistory);
        return list;
    }

    /**
     * 存储群聊消息
     *
     * @param messageContent 群聊消息内容
     */
    @Transactional
    public void storeGroupMessage(GroupChatMessageContent messageContent) {
        // 提取消息体
        ImMessageBody imMessageBody = extractMessageBody(messageContent);
        // 创建存储群聊消息的 DTO 对象
        DoStoreGroupMessageDto dto = new DoStoreGroupMessageDto();
        dto.setMessageBody(imMessageBody);
        dto.setGroupChatMessageContent(messageContent);
        // 将 DTO 对象转换为 JSON 字符串并发送到 RabbitMQ 中
        rabbitTemplate.convertAndSend(Constants.RabbitConstants.StoreGroupMessage, "", JSONObject.toJSONString(dto));
        // 将消息体的消息 ID 赋值给消息内容的消息 ID
        messageContent.setMessageKey(imMessageBody.getMessageKey());
    }

    /**
     * 将消息内容转化为群聊消息历史记录
     *
     * @param messageContent    群聊消息内容
     * @param messageBodyEntity 消息体
     * @return ImGroupMessageHistoryEntity 群聊消息历史记录
     */
    public ImGroupMessageHistoryEntity extractToGroupMessageHistory(GroupChatMessageContent messageContent, ImMessageBodyEntity messageBodyEntity) {
        // 创建一个 ImGroupMessageHistoryEntity 对象作为返回值
        ImGroupMessageHistoryEntity result = new ImGroupMessageHistoryEntity();
        // 将 messageContent 对象中的属性值拷贝到 result 对象中
        BeanUtils.copyProperties(messageContent, result);
        // 设置 result 对象的 groupId 属性为 messageContent 对象的 groupId 属性值
        result.setGroupId(messageContent.getGroupId());
        // 设置 result 对象的 messageKey 属性为 messageBodyEntity 对象的 messageKey 属性值
        result.setMessageKey(messageBodyEntity.getMessageKey());
        // 设置 result 对象的 createTime 属性为当前时间的毫秒数
        result.setCreateTime(System.currentTimeMillis());
        // 返回转换后的 ImGroupMessageHistoryEntity 对象
        return result;
    }

    /**
     * 将消息存入 Redis 缓存
     *
     * @param appId          应用 ID
     * @param messageId      消息 ID
     * @param messageContent 消息内容
     */
    public void setMessageFromMessageIdCache(Integer appId, String messageId, Object messageContent) {
        // 拼接 key，格式为：appId:cacheMessage:messageId
        String key = appId + ":" + Constants.RedisConstants.cacheMessage + ":" + messageId;
        // 将消息内容转换为 JSON 字符串并存入 Redis 缓存中，设置过期时间为 300 秒
        stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(messageContent), 300, TimeUnit.SECONDS);
    }

    /**
     * 从 Redis 缓存中获取消息
     *
     * @param appId     应用 ID
     * @param messageId 消息 ID
     * @param clazz     消息类型
     * @return T 消息
     */
    public <T> T getMessageFromMessageIdCache(Integer appId, String messageId, Class<T> clazz) {
        // 拼接 key，格式为：appId:cacheMessage:messageId
        String key = appId + ":" + Constants.RedisConstants.cacheMessage + ":" + messageId;
        // 从 Redis 缓存中获取指定 key 的值
        String msg = stringRedisTemplate.opsForValue().get(key);
        // 如果缓存中不存在指定 key 的值，则返回 null
        if (StringUtils.isBlank(msg)) {
            return null;
        }
        // 将获取到的消息内容转换为指定类型的对象并返回
        return JSONObject.parseObject(msg, clazz);
    }

    /**
     * 存储单人离线消息
     *
     * @param offlineMessage 离线消息内容
     */
    public void storeOfflineMessage(OfflineMessageContent offlineMessage) {
        // 找到fromId的队列
        String fromKey = offlineMessage.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + offlineMessage.getFromId();
        // 找到toId的队列
        String toKey = offlineMessage.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + offlineMessage.getToId();
        ZSetOperations<String, String> operations = stringRedisTemplate.opsForZSet();
        //判断 队列中的数据是否超过设定值
        if (operations.zCard(fromKey) > appConfig.getOfflineMessageCount()) {
            operations.removeRange(fromKey, 0, 0);
        }
        offlineMessage.setConversationId(conversationService.convertConversationId(
                ConversationTypeEnum.P2P.getCode(), offlineMessage.getFromId(), offlineMessage.getToId()
        ));
        // 插入 数据 根据messageKey 作为分值
        operations.add(fromKey, JSONObject.toJSONString(offlineMessage), offlineMessage.getMessageKey());

        //判断 队列中的数据是否超过设定值
        if (operations.zCard(toKey) > appConfig.getOfflineMessageCount()) {
            operations.removeRange(toKey, 0, 0);
        }
        offlineMessage.setConversationId(conversationService.convertConversationId(
                ConversationTypeEnum.P2P.getCode(), offlineMessage.getToId(), offlineMessage.getFromId()
        ));
        // 插入 数据 根据messageKey 作为分值
        operations.add(toKey, JSONObject.toJSONString(offlineMessage), offlineMessage.getMessageKey());

    }

    /**
     * 存储群聊离线消息
     *
     * @param offlineMessage 离线消息内容
     * @param memberIds      群聊成员 ID 列表
     */
    public void storeGroupOfflineMessage(OfflineMessageContent offlineMessage, List<String> memberIds) {
        ZSetOperations<String, String> operations = stringRedisTemplate.opsForZSet();
        //判断 队列中的数据是否超过设定值
        offlineMessage.setConversationType(ConversationTypeEnum.GROUP.getCode());
        for (String memberId : memberIds) {
            // 找到toId的队列
            String toKey = offlineMessage.getAppId() + ":" + Constants.RedisConstants.OfflineMessage + ":" + memberId;
            offlineMessage.setConversationId(conversationService.convertConversationId(
                    ConversationTypeEnum.GROUP.getCode(), memberId, offlineMessage.getToId()
            ));
            if (operations.zCard(toKey) > appConfig.getOfflineMessageCount()) {
                operations.removeRange(toKey, 0, 0);
            }
            // 插入 数据 根据messageKey 作为分值
            operations.add(toKey, JSONObject.toJSONString(offlineMessage), offlineMessage.getMessageKey());
        }
    }

}